package org.ubc.cs410.disposal2020.client.view;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.ubc.cs410.disposal2020.client.MapService;
import org.ubc.cs410.disposal2020.client.MapServiceAsync;
import org.ubc.cs410.disposal2020.client.dto.BuildingBoundaryDto;
import org.ubc.cs410.disposal2020.client.dto.MapDto;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.control.LargeMapControl;
import com.google.gwt.maps.client.control.MapTypeControl;
import com.google.gwt.maps.client.event.MapClickHandler;
import com.google.gwt.maps.client.event.MarkerDragEndHandler;
import com.google.gwt.maps.client.event.MarkerDragStartHandler;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.Size;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.Overlay;
import com.google.gwt.maps.client.overlay.Polygon;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitHandler;

/*
 * New feature implemented
 * An admin can create a new map with this class
 */

public class MapCreator implements EntryPoint {

	// createRobot and loadBldBtn are for testing
	private Button loadBldBtn;
	private Button createRobot;

	private Button addGarbage;

	private Label titleLabel;
	private TextBox titleText;

	private Label descLabel;
	private TextBox descText;

	private Label totalGarbageLabel;
	private TextBox totalGarbageText;

	private Label loadMapLabel;
	private ListBox listBox;
	private Button loadMapButton;
	private Button deleteMapButton;

	private Button addBldBtn;
	private Button cancelAddBldBtn;
	private Button createMapBtn;

	private MapView mapView;

	private boolean isAddBuildingMode = false;

	private List<LatLng> tempLatLngList;
	private List<Overlay> tempMarkerList;
	private Polygon tempCircle;

	private List<BuildingBoundaryDto> buildingList;

	private int id = 0;

	private int screenwidth = 768;
	private int screenheight = 1024;

	private ArrayList<Marker> robotIcons;
	private LatLng startPt;
	private ArrayList<Marker> trashIcons;

	private FileUpload upload;
	private FormPanel form;

	private MapServiceAsync mapService;

	@Override
	public void onModuleLoad() {
		robotIcons = new ArrayList<Marker>();
		trashIcons = new ArrayList<Marker>();

		// loadBldBtn and createRobot for testing purpose
		loadBldBtn = new Button("Load Building");
		createRobot = new Button("Add a robot");

		addGarbage = new Button("Add a garbage");

		titleLabel = new Label("Title:");
		titleText = new TextBox();

		descLabel = new Label("Description:");
		descText = new TextBox();

		totalGarbageLabel = new Label("Total Garbage:");
		totalGarbageText = new TextBox();

		loadMapLabel = new Label("Load Map Title:");
		listBox = new ListBox();

		listBox.addItem("Loading...");
		loadMapButton = new Button("Load");
		deleteMapButton = new Button("Delete");

		tempLatLngList = new ArrayList<LatLng>();
		tempMarkerList = new ArrayList<Overlay>();
		buildingList = new ArrayList<BuildingBoundaryDto>();

		init();
		loadGameToModel("");

		LatLng ubcCampus = LatLng.newInstance(49.26282, -123.24085);

		MapOptions mapOptions = MapOptions.newInstance();
		// ToDo: read in dynamic size.
		mapOptions.setSize(Size.newInstance(screenwidth, screenheight));

		VerticalPanel mainPanel = new VerticalPanel();
		HorizontalPanel controlPanel = new HorizontalPanel();

		HorizontalPanel controlPanel2 = new HorizontalPanel();

		Grid grid = new Grid(3, 2);
		grid.setWidget(0, 0, titleLabel);
		grid.setWidget(0, 1, titleText);

		grid.setWidget(1, 0, descLabel);
		grid.setWidget(1, 1, descText);

		grid.setWidget(2, 0, totalGarbageLabel);
		grid.setWidget(2, 1, totalGarbageText);

		mapView = new MapView(ubcCampus, 14, mapOptions);
		mapView.getMap().addControl(new MapTypeControl());
		mapView.getMap().addControl(new LargeMapControl());

		mapView.getMap().addMapClickHandler(new MapClickHandler() {
			public void onClick(MapClickEvent event) {
				LatLng lat = event.getLatLng();
				tempLatLngList.add(lat);
				Marker marker = new Marker(lat);
				mapView.getMap().addOverlay(marker);
				tempMarkerList.add(marker);
			}
		});

		addBldBtn = new Button("Add Building");
		cancelAddBldBtn = new Button("Cancel Add Building");
		cancelAddBldBtn.setEnabled(false);
		createMapBtn = new Button("Create Map");

		cancelAddBldBtn.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				tempLatLngList.clear();
				for (Overlay overlay : tempMarkerList) {
					mapView.getMap().removeOverlay(overlay);
				}
				tempMarkerList.clear();
			}
		});

		deleteMapButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				final String mapName = listBox.getItemText(listBox
						.getSelectedIndex());
				if (!"".equals(mapName)) {
					mapService.deleteMap(mapName, new AsyncCallback<MapDto>() {

						@Override
						public void onFailure(Throwable caught) {
							Window.alert("Failed to delete Map: " + mapName);

						}

						@Override
						public void onSuccess(MapDto result) {
							Window.alert("Deleted Map: " + mapName);
							loadGameToModel("");
						}

					});
				}

			}
		});

		loadMapButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				final String mapName = listBox.getItemText(listBox
						.getSelectedIndex());
				if (!"".equals(mapName)) {
					mapService.loadMap(mapName, new AsyncCallback<MapDto>() {

						@Override
						public void onFailure(Throwable caught) {
							// TODO Auto-generated method stub

						}

						@Override
						public void onSuccess(MapDto mapDto) {
							if (mapDto != null) {
								mapView.resetMapLocation(mapDto
										.getCenterLatitude(), mapDto
										.getCenterLongitude(), mapDto
										.getZoomLevel());
								mapView.clearAllOverylay();

								for (BuildingBoundaryDto dto : mapDto
										.getBuildingList()) {
									mapView.addOverlay(dto);
								}

								buildingList.clear();
								buildingList.addAll(mapDto.getBuildingList());
							}
						}
					});
				}
			}
		});

		addBldBtn.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				isAddBuildingMode = !isAddBuildingMode;
				if (isAddBuildingMode) {
					addBldBtn.setText("Stop Add Building");
					tempLatLngList.clear();
					for (Overlay overlay : tempMarkerList) {
						mapView.getMap().removeOverlay(overlay);
					}
					tempMarkerList.clear();
					cancelAddBldBtn.setEnabled(true);
					createMapBtn.setEnabled(false);
					loadMapButton.setEnabled(false);
				} else {
					addBldBtn.setText("Add Building");

					BuildingBoundaryDto buildingBoundary = createBuildingBoundaryDto();
					buildingList.add(buildingBoundary);

					mapView.addOverlay(buildingBoundary);
					cancelAddBldBtn.setEnabled(false);
					createMapBtn.setEnabled(true);
					loadMapButton.setEnabled(true);
				}

			}

		});

		createMapBtn.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				int zoomLevel = mapView.getMap().getZoomLevel();
				LatLng centerLocation = mapView.getCenterLocation();

				String title = titleText.getText();
				String desc = descText.getText();
				String totalGarbageStr = totalGarbageText.getText();
				int totalGarbage = 0;
				try {
					totalGarbage = Integer.parseInt(totalGarbageStr);
				} catch (Exception ex) {
					DialogBox dialogBox = new DialogBox(true, true);
					dialogBox.setText("Invalid total garbage input: "
							+ totalGarbageStr);
					dialogBox.show();
					return;
				}

				MapDto map = new MapDto(title, desc, totalGarbage,
						(float) centerLocation.getLatitude(),
						(float) centerLocation.getLongitude(), zoomLevel,
						buildingList);

				mapService.createMap(map, new AsyncCallback<Void>() {

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub

					}

					@Override
					public void onSuccess(Void result) {

						loadGameToModel(titleText.getText());
						// loadMapText.setText(titleText.getText());
						titleText.setText("");
						descText.setText("");
						totalGarbageText.setText("");
						mapView.clearAllOverylay();
						buildingList.clear();
					}
				});

			}
		});

		// testing purpose
		loadBldBtn.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				//
			}
		});

		createRobot.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {

				List<Float> lats = new ArrayList<Float>();
				List<Float> longs = new ArrayList<Float>();
				lats = new ArrayList<Float>();
				longs = new ArrayList<Float>();
				// Dempster - Hard Coded Robot Position
				longs.add(-123.248032f);
				lats.add(49.26104f);
				longs.add(-123.248291f);
				lats.add(49.261356f);
				longs.add(-123.248032f);
				lats.add(49.26145600000001f);
				longs.add(-123.24778f);
				lats.add(49.261124f);
				longs.add(-123.248032f);
				lats.add(49.26104f);

				// LatLng[] latLngArray = new LatLng[5];
				ArrayList<LatLng> latLngArray = new ArrayList<LatLng>();
				for (int i = 0; i < 5; i++) {
					latLngArray.add(LatLng.newInstance(lats.get(i)
							.doubleValue(), longs.get(i).doubleValue()));
				}

				BuildingBoundaryDto building = new BuildingBoundaryDto(1100,
						lats, longs);

				// LatLng factoryLoc = building.findCentre(latLngArray);

				LatLng factoryLoc = LatLng.newInstance(49.2612, -123.248032);
				final Marker rbtTemp = mapView.addIcon(factoryLoc,
						"/robot.png", true);

				rbtTemp.addMarkerDragStartHandler(new MarkerDragStartHandler() {
					public void onDragStart(MarkerDragStartEvent event) {
						startPt = rbtTemp.getLatLng();
						tempCircle = mapView.addCircle(startPt);
					}
				});

				rbtTemp.addMarkerDragEndHandler(new MarkerDragEndHandler() {
					public void onDragEnd(MarkerDragEndEvent event) {
						LatLng endPt = rbtTemp.getLatLng();
						double dis = startPt.distanceFrom(endPt);
						if (dis > 200) {
							rbtTemp.setLatLng(startPt);
						} else {
							rbtTemp.setLatLng(endPt);

							for (int i = 0; i < trashIcons.size(); i++) {
								Marker trashIterator = trashIcons.get(i);
								if (trashIterator != null) {
									if (trashIterator.getLatLng().distanceFrom(
											endPt) < 50) {
										mapView.getMap().removeOverlay(
												trashIterator);
										// TODO: add mark to the user
									}
								}
							}
						}

						// TODO: add robot in the db
						mapView.getMap().removeOverlay(tempCircle);
					}
				});

				robotIcons.add(rbtTemp);
			}
		});

		addGarbage.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				// hardcoded building centre
				LatLng buildingCtr = LatLng.newInstance(49.261028, -123.249001);
				final Marker trashTemp = mapView.addIcon(buildingCtr,
				"/garbage.png");

				/*
				 * trashTemp.addMarkerClickHandler(new MarkerClickHandler() {
				 * public void onClick(MarkerClickEvent event) {
				 * 
				 * } });
				 */

				trashIcons.add(trashTemp);
			}
		});

		controlPanel.add(addBldBtn);
		controlPanel.add(cancelAddBldBtn);

		controlPanel2.add(loadMapLabel);
		controlPanel2.add(listBox);
		controlPanel2.add(loadMapButton);
		controlPanel2.add(deleteMapButton);

		mainPanel.add(grid);
		mainPanel.add(controlPanel);
		mainPanel.add(controlPanel2);
		mainPanel.add(createMapBtn);

		HorizontalPanel controlPanel3 = new HorizontalPanel();
		// testing purpose
		controlPanel3.add(new Label("For demo:"));
		controlPanel3.add(loadBldBtn);
		controlPanel3.add(createRobot);
		controlPanel3.add(addGarbage);
		controlPanel3.add(new Label("Not sure why we need it.."));
		mainPanel.add(controlPanel3);

		upload = new FileUpload();
		form = new FormPanel();
		form.setAction("/admin/data");
		form.setEncoding(FormPanel.ENCODING_MULTIPART);
		form.setMethod(FormPanel.METHOD_POST);
		HorizontalPanel uploadPanel = new HorizontalPanel();
		uploadPanel.add(new Label("Import CSV file"));
		form.setWidget(uploadPanel);
		upload.setName("uploadFormElement");
		uploadPanel.add(upload);
		// Add a 'submit' button.
		uploadPanel.add(new Button("Submit", new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				form.submit();
			}
		}));
		form.addSubmitHandler(new SubmitHandler() {

			@Override
			public void onSubmit(SubmitEvent event) {

			}
		});

		form.addSubmitCompleteHandler(new SubmitCompleteHandler() {

			@Override
			public void onSubmitComplete(SubmitCompleteEvent event) {
				loadGameToModel("");
			}
		});

		mainPanel.add(form);

		mainPanel.add(mapView);

		RootPanel.get("mapCreatorPanel").add(mainPanel);
	}

	private void init() {
		mapService = (MapServiceAsync) GWT.create(MapService.class);
		ServiceDefTarget endpoint = (ServiceDefTarget) mapService;
		endpoint.setServiceEntryPoint("/services/map");
	}

	private void loadGameToModel(final String name) {
		mapService.getAllMapNames(new AsyncCallback<Collection<String>>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Collection<String> result) {
				listBox.clear();
				for (String game : result) {
					listBox.addItem(game, game);
				}
				if (name.length() > 0) {
					for (int i = 0; i < listBox.getItemCount(); i++) {
						if (name.equalsIgnoreCase(listBox.getItemText(i))) {
							listBox.setSelectedIndex(i);
						}
					}
				} else {
					listBox.setSelectedIndex(listBox.getItemCount() - 1);
				}

			}
		});
	}

	private BuildingBoundaryDto createBuildingBoundaryDto() {

		List<Float> latitudes = new ArrayList<Float>();
		List<Float> longitudes = new ArrayList<Float>();

		for (LatLng latLng : tempLatLngList) {
			latitudes.add((float) latLng.getLatitude());
			longitudes.add((float) latLng.getLongitude());
		}

		BuildingBoundaryDto buildingBoundaryDto = new BuildingBoundaryDto(id++,
				latitudes, longitudes);
		return buildingBoundaryDto;
	}

}
